package com.girant.logger.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.girant.dict.api.DictApi;
import com.girant.log.enums.LogCategoryEnums;
import com.girant.logger.config.BoostConfig;
import com.girant.logger.entity.domain.LogDocumentDO;
import com.girant.logger.entity.dto.BusinessLogDTO;
import com.girant.logger.entity.vo.show.LogShowVO;
import com.girant.logger.mapper.LogMapper;
import com.girant.logger.service.LogInfoService;
import com.girant.logger.utils.SearchUtil;
import com.girant.model.enums.ClientTypeEnums;
import com.girant.mybatis.entity.BasePage;
import lombok.extern.slf4j.Slf4j;
import org.dromara.easyes.core.biz.EsPageInfo;
import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
import static com.girant.logger.utils.SearchUtil.getEnumLabel;

@Slf4j
@Service
public class LogInfoServiceImpl implements LogInfoService {
    @Autowired
    private  DiscoveryClient discoveryClient;
    @Autowired
    private LogMapper logMapper;
    @Autowired
    private DictApi dictApi;

    @Autowired
    private BoostConfig boostConfig;

    @Value("${es.min.score:0.2}")
    private Double minScore;


    @Override
    public BasePage<LogShowVO> queryLogPage(BusinessLogDTO request) {
        try {
            List<String> serverNames = discoveryClient.getServices();
            Map<String, List<ServiceInstance>> serviceMap = new HashMap<>();

            Optional<String> found = serverNames.stream()
                    .filter(s -> s.equals(request.getServerName()))
                    .findFirst();

            for (Map.Entry<String, List<ServiceInstance>> entry : serviceMap.entrySet()) {
                String serviceName = entry.getKey();
                List<ServiceInstance> instances = entry.getValue();

                System.out.println("服务名称: " + serviceName);
                System.out.println("实例数量: " + instances.size());

                for (ServiceInstance instance : instances) {
                    System.out.println("\t实例URI: " + instance.getUri());
                    System.out.println("\t实例ID: " + instance.getInstanceId());
                    System.out.println("\t元数据: " + instance.getMetadata());
                }
                System.out.println();
            }
            // 1. 构建查询条件
            LambdaEsQueryWrapper<LogDocumentDO> wrapper = buildBoolQuery(request);

            // 2. 设置排序
            wrapper.orderByDesc(LogDocumentDO::getOperationTime);

            if (StrUtil.isNotBlank(request.getKeyword())){
                wrapper.minScore(minScore);
            }

            // 3. 创建分页参数
            long from = (request.getPageNum() - 1) * request.getPageSize(); // 正确计算：(页码-1)*每页条数
            long size = request.getPageSize();
            log.info("执行ES查询：索引={}, 分页参数[pageNum={}, pageSize={}]", "business-logs-*", from, size);

            // 4. 执行分页查询
            EsPageInfo<LogDocumentDO> esPageInfo = logMapper.pageQuery(wrapper,(int)from,(int)size);

            // 5. 处理查询结果
            long total = esPageInfo.getTotal();
            List<LogDocumentDO> documents = esPageInfo.getList();

            // 6. 转换为VO
            List<LogShowVO> records = documents.stream()
                    .map(this::convertToVO)
                    .collect(Collectors.toList());

            // 7. 获取分页信息
            long totalPages = esPageInfo.getPages();
            log.info("ES查询总命中数：{}条, 当前页数据量：{}条,分页数量{}页", total, documents.size(),totalPages);
            return new BasePage<>(
                    records,
                    request.getPageNum(),
                    request.getPageSize(),
                    total,
                    totalPages
            );

        } catch (Exception e) {
            log.error("ES查询失败，异常信息：", e);
            return new BasePage<>(
                    Collections.emptyList(),
                    request.getPageNum(),
                    request.getPageSize(),
                    0L,
                    0L
            );
        }
    }

    // 转换实体为VO的辅助方法
    private LogShowVO convertToVO(LogDocumentDO document) {

        //1. 客户端类型及标签
        String clientTypeCode = document.getClientType();
        document.setClientType(clientTypeCode);
        ClientTypeEnums enums = EnumUtil.getBy(ClientTypeEnums::getValue, "01");
        document.setClientTypeLabel(getEnumLabel(
                ClientTypeEnums.values(),
                clientTypeCode,
                ClientTypeEnums::getValue,
                ClientTypeEnums::getLabel
        ));

        // 2. 业务分类及标签
        String categoryCode = document.getCategory();
        document.setCategory(categoryCode);
        document.setCategoryLabel(getEnumLabel(
                LogCategoryEnums.values(),
                categoryCode,
                LogCategoryEnums::getValue,
                LogCategoryEnums::getLabel
        ));
        //3.字典赋值ServerNameLabel
        document.setServerNameLabel(dictApi.getSecureDictLabelByKey("sysLogServerName",document.getServerName()));

        //4. 基础属性拷贝
        LogShowVO vo = BeanUtil.copyProperties(document, LogShowVO.class);
        return vo;
    }

    private LambdaEsQueryWrapper<LogDocumentDO> buildBoolQuery(BusinessLogDTO request) {
        LambdaEsQueryWrapper<LogDocumentDO> wrapper = new LambdaEsQueryWrapper<>();

        if (ObjectUtil.isNotEmpty(request.getOperationStartTime())) {
            wrapper.ge(LogDocumentDO::getOperationTime, DateUtil.format(request.getOperationStartTime(), DatePattern.NORM_DATETIME_MS_PATTERN));//大于等于，并且转化格式
        }
        // 处理结束时间
        if (ObjectUtil.isNotEmpty(request.getOperationEndTime())) {
            wrapper.le(LogDocumentDO::getOperationTime, DateUtil.format(request.getOperationEndTime(), DatePattern.NORM_DATETIME_MS_PATTERN));//小于等于，并且转化格式
        }

        // 2. IP模糊匹配（CIDR格式）
        if (StrUtil.isNotBlank(request.getSourceIp())) {//
            String cidr = SearchUtil.convertIpPrefixToCidr(request.getSourceIp());
            wrapper.eq(LogDocumentDO::getSourceIp, cidr);
        }

        // 3. 精确匹配字段
        if (StrUtil.isNotBlank(request.getTraceId())) {//
            wrapper.eq(LogDocumentDO::getTraceId, request.getTraceId());
        }
        if (StrUtil.isNotBlank(request.getSpanId())) {//
            wrapper.eq(LogDocumentDO::getSpanId, request.getSpanId());
        }
        if (StrUtil.isNotBlank(request.getServerName())) {//
            wrapper.eq(LogDocumentDO::getServerName, request.getServerName());
        }

        if (StrUtil.isNotBlank(request.getClientType())) {//补充
            wrapper.eq(LogDocumentDO::getClientType, request.getClientType());
        }

        if (StrUtil.isNotBlank(request.getCategory())) {//补充
            wrapper.eq(LogDocumentDO::getCategory, request.getCategory());
        }

        if (StrUtil.isNotBlank(request.getLogLevel())) {//补充
            wrapper.eq(LogDocumentDO::getLogLevel, request.getLogLevel());
        }


        if (StrUtil.isNotBlank(request.getKeyword())) {
            wrapper.should(w -> w.match(LogDocumentDO::getTitle, request.getKeyword(), boostConfig.getErrorInfo()));
            wrapper.should(w -> w.match(LogDocumentDO::getContent, request.getKeyword(), boostConfig.getContent()));
            wrapper.should(w -> w.eq(LogDocumentDO::getOperationNickname, request.getKeyword(), boostConfig.getOperationNickname()));
            wrapper.should(w -> w.eq(LogDocumentDO::getClientVersion, request.getKeyword(), boostConfig.getClientVersion()));
            wrapper.should(w -> w.eq(LogDocumentDO::getStackTrace, request.getKeyword(), boostConfig.getStackTrace()));
            wrapper.should(w -> w.match(LogDocumentDO::getRequestParams, request.getKeyword(), boostConfig.getRequestParams()));
            wrapper.should(w -> w.match(LogDocumentDO::getClientBrowser, request.getKeyword(), boostConfig.getClientBrowser()));
        }

        return wrapper;
    }


}

